﻿#include "stdafx.h"
#include "HttpManger.h"
#include "AppHelper.h"

// Qt lib import
#include <QEventLoop>
#include <QTimer>
#include <QUrl>
#include <QHostInfo>
#include <QJsonObject>
#include <QJsonArray>
#include <QJsonDocument>
#include <QBuffer>

QNetworkAddressEntry getNetworkAddressEntry()
{
    return getNetworkAddressEntryWithNetworkInterface().first;
}

QPair< QNetworkAddressEntry, QNetworkInterface > getNetworkAddressEntryWithNetworkInterface(const bool &ridVm)
{
    for (const auto &interface: QNetworkInterface::allInterfaces())
    {
        if ( interface.flags() != (QNetworkInterface::IsUp
                                 | QNetworkInterface::IsRunning
                                 | QNetworkInterface::CanBroadcast
                                 | QNetworkInterface::CanMulticast ) ) { continue; }

        if ( ridVm && interface.humanReadableName().startsWith( "vm" ) ) { continue; }

        for( const auto &entry: interface.addressEntries() )
        {
            if ( entry.ip().toIPv4Address() )
            {
                return { entry, interface };
            }
        }
    }

    return { };
}

QString getHostName()
{
#ifdef Q_OS_MAC
    return QHostInfo::localHostName().replace(".local", "");
#endif
    return QHostInfo::localHostName();
}

// HTTP
bool HTTP::get(const QNetworkRequest &request, QByteArray &target, const int &timeout)
{
    target.clear();

    QEventLoop eventLoop;
    auto reply = manage_.get(request);
    bool failFlag = false;

    this->handle(
        reply,
        timeout,
        [&](const QByteArray &data)
        {
            target = data;
            eventLoop.exit(1);
        },
        [&](const QNetworkReply::NetworkError &)
        {
            eventLoop.exit(0);
        },
        [&]()
        {
            failFlag = true;
            eventLoop.exit(0);
        }
    );

    return eventLoop.exec() && !failFlag;
}

void HTTP::get(const QNetworkRequest &request,
               const std::function<void (const QByteArray &)> &onFinished,
               const std::function<void (const QNetworkReply::NetworkError &)> &onError,
               const int &timeout)
{
    auto reply = manage_.get(request);

    this->handle(
        reply,
        timeout,
        onFinished,
        onError,
        [=]()
        {
            onError(QNetworkReply::TimeoutError);
        }
    );
}

bool HTTP::post(const QNetworkRequest &request, const QByteArray &appendData, QByteArray &target, const int &timeout)
{
    target.clear();

    QEventLoop eventLoop;
    auto reply = manage_.post(request, appendData);
    bool failFlag = false;

    this->handle(
        reply,
        timeout,
        [&](const QByteArray &data)
        {
            target = data;
            eventLoop.exit(1);
        },
        [&](const QNetworkReply::NetworkError &)
        {
            eventLoop.exit(0);
        },
        [&]()
        {
            failFlag = true;
            eventLoop.exit(0);
        }
    );

    return eventLoop.exec() && !failFlag;
}

void HTTP::post(const QNetworkRequest &request,
                const QByteArray &appendData,
                const std::function<void (const QByteArray &)> &onFinished,
                const std::function<void (const QNetworkReply::NetworkError &)> &onError,
                const int &timeout)
{
    auto reply = manage_.post(request, appendData);

    this->handle(
        reply,
        timeout,
        onFinished,
        onError,
        [=]()
        {
            onError( QNetworkReply::TimeoutError );
        }
    );
}

QPair< bool, QByteArray > HTTP::get(const QString &url, const int &timeout)
{
    QNetworkRequest networkRequest( ( QUrl( url ) ) );
    QByteArray buf;

    const auto &&flag = HTTP().get( networkRequest, buf, timeout );

    return { flag, buf };
}

QPair< bool, QByteArray > HTTP::get(const QNetworkRequest &request, const int &timeout)
{
    QByteArray buf;
    HTTP http;

    const auto &&flag = http.get( request, buf, timeout );

    return { flag, buf };
}

QPair< bool, QByteArray > HTTP::post(const QString &url, const QByteArray &appendData, const int &timeout)
{
    QNetworkRequest networkRequest( ( QUrl( url ) ) );
    QByteArray buf;

    networkRequest.setRawHeader( "Content-Type", "application/x-www-form-urlencoded" );

    const auto &&flag = HTTP().post( networkRequest, appendData, buf, timeout );

    return { flag, buf };
}

QPair< bool, QByteArray > HTTP::post(const QNetworkRequest &request, const QByteArray &appendData, const int &timeout)
{
    QByteArray buf;
    HTTP http;

    const auto &&flag = http.post( request, appendData, buf, timeout );

    return { flag, buf };
}

void HTTP::handle(QNetworkReply *reply, const int &timeout,
                  const std::function<void (const QByteArray &)> &onFinished,
                  const std::function<void (const QNetworkReply::NetworkError &)> &onError,
                  const std::function<void ()> &onTimeout)
{
    QTimer *timer = NULL;
    if (timeout)
    {
        timer = new QTimer;
        timer->setSingleShot(true);
        QObject::connect(timer, &QTimer::timeout, [=]()
        {
            onTimeout();
            timer->deleteLater();
        });
        timer->start(timeout);
    }

    QObject::connect(reply, &QNetworkReply::finished, [=]()
    {
        if (timer)
        {
            timer->deleteLater();
        }
        onFinished(reply->readAll());
    });

    QObject::connect(reply, (void(QNetworkReply::*)(QNetworkReply::NetworkError))&QNetworkReply::error, [=](const QNetworkReply::NetworkError &code)
    {
        if (timer)
        {
            timer->deleteLater();
        }
        onError(code);
    });
}

/***********************************************************************************************************************************/

int HttpManger::iType = 0;
QString HttpManger::strURL;

HttpManger::HttpManger(QObject *parent)
: QObject(parent)
{
	initData();
}

HttpManger::~HttpManger()
{

}

/***********************************************************************************************************************************/
bool HttpManger::exec(const QString & SQL, QString & Msg)
{
	QString execUrl = strURL + "/update";
	QByteArray arraySQL;

	if (iType == 1)
	{
		//连接Java服务端
		QString strSQL = "sql=" + SQL;
		arraySQL = strSQL.toLocal8Bit();
	}
	else
	{
		arraySQL = SQL.toLocal8Bit();
	}

	const auto &&reply = HTTP::post(execUrl, arraySQL);

	if (reply.first)
	{
		return parse(reply.second, Msg);
	}
	else
	{
		Msg = tr("Request time out, Please Check Connection!");
		return false;
	}
	
}

bool HttpManger::select(const QString & SQL, QRecord & dsRecord, QString & Msg)
{
	QString execUrl = strURL + "/select";
	QByteArray arraySQL;

	if (iType == 1)
	{
		//连接Java服务端
		QString strSQL = "sql=" + SQL;
		strSQL.replace("%", "%25");
		strSQL.replace("+", "%2B");
		arraySQL = strSQL.toLocal8Bit();
	}
	else
	{
		arraySQL = SQL.toLocal8Bit();
	}

	const auto &&reply = HTTP::post(execUrl, arraySQL);

	if (reply.first)
	{
		return serialize(reply.second, dsRecord, Msg);
	}
	else
	{
		Msg = tr("Request time out, Please Check Connection!");
		return false;
	}
}

bool HttpManger::loadPicture(const QString & strFiles, const QString &strServerFiles, QString & Msg)
{
	QString execUrl = strURL + "/LoadPicture";
	QByteArray arrayServerFiles;
	arrayServerFiles = strServerFiles.toLocal8Bit();

	const auto &&reply = HTTP::post(execUrl, arrayServerFiles);

	if (reply.first)
	{
		QImage imagePicture = QImage::fromData(reply.second);
		imagePicture.data_ptr();
		bool ret = imagePicture.save(strFiles);
		return true;
	}
	else
	{
		Msg = tr("Request time out, Please Check Connection!");
		return false;
	}
}

bool HttpManger::savePicture(const QString & strFiles, const QString &strServerFiles, QString & Msg)
{
	QString execUrl = strURL + QString("/SavePicture?filename=%1").arg(strServerFiles);
	QByteArray arrayServerFiles;
	arrayServerFiles = strServerFiles.toLocal8Bit();

	int index = strFiles.lastIndexOf(".");
	QString strType = strFiles.mid(index);
	char * pFormat = nullptr;
	if (strType.compare(".png", Qt::CaseInsensitive) == 0)
	{
		pFormat = "PNG";
	}
	else if (strType.compare(".jpg", Qt::CaseInsensitive) == 0)
	{
		pFormat = "JPG";
	}
	else if (strType.compare(".bmp", Qt::CaseInsensitive) == 0)
	{
		pFormat = "BMP";
	}
	else if (strType.compare(".gif", Qt::CaseInsensitive) == 0)
	{
		pFormat = "GIF";
	}

	QImage image;
	image.load(strFiles);
	QByteArray imageByteArray;
	QBuffer buffer(&imageByteArray);
	buffer.open(QIODevice::WriteOnly);
	image.save(&buffer, pFormat);

	const auto &&reply = HTTP::post(execUrl, imageByteArray);

	if (reply.first)
	{
		//QImage &imagePicture = QImage::fromData(reply.second);
		//imagePicture.data_ptr();
		//bool ret = imagePicture.save(strFiles);
		return true;
	}
	else
	{
		Msg = tr("Request time out, Please Check Connection!");
		return false;
	}
}

/***********************************************************************************************************************************/
void HttpManger::initData()
{
	if (strURL.isEmpty())
	{
		if (gMapConfigs.count() == 0)
		{
			QSettings * config = getConfig();
			readConfig(*config);
		}

		if (gMapConfigs.contains("SERVER/URL"))
		{
			strURL = gMapConfigs.value("SERVER/URL").toString();
			iType = gMapConfigs.value("SERVER/TYPE").toInt();
		}
	}
}

bool HttpManger::parse(const QByteArray & data, QString & strMsg)
{
	const QJsonDocument & jsonDoc = QJsonDocument::fromJson(data);
	if (jsonDoc.isObject())
	{
		const QJsonObject & Datas = jsonDoc.object();
		if (!Datas.contains("Success"))
		{
			if (!Datas.contains("Error"))
			{
				strMsg = tr("can not found return value!");
			}
			else
			{
				strMsg = Datas.value("Error").toString();
			}
			return false;
		}
		else
		{
			strMsg = Datas.value("Success").toString();
			return true;
		}
	}

	strMsg = tr("Data parse failed, data is not a json object");
	return false;
}

bool HttpManger::serialize(const QByteArray & data, QRecord & dsRecord, QString & strMsg)
{
	dsRecord.clear();

	const QJsonDocument & jsonDoc = QJsonDocument::fromJson(data);

	if (jsonDoc.isObject())
	{
		const QJsonObject & Datas = jsonDoc.object();
		const QJsonValue & jsonFileds = Datas.value("fields");
		if (jsonFileds.isArray())
		{
			const QJsonArray & fields = jsonFileds.toArray();
			for (int col = 0; col < fields.size(); ++col)
			{
				const QJsonValue & jsonCol = fields.at(col);
				if (jsonCol.isObject())
				{
					const QJsonObject & colData = jsonCol.toObject();
					const QString & colName = colData.value("name").toString();
					int colType = colData.value("type").toInt();

					ColumnInfo info;
					info.Name = colName;
					info.Type = (COLUMN_TYPE)colType;
					info.Flag = CF_NONE;
					dsRecord.addCol(info);
				}
			}

			const QJsonValue & jsonRecords = Datas.value("records");
			if (jsonRecords.isArray())
			{
				const QJsonArray & records = jsonRecords.toArray();
				for (int row = 0; row < records.size(); ++row)
				{
					const QJsonValue & jsonRow = records.at(row);
					if (jsonRow.isArray())
					{
						const QJsonArray & rowData = jsonRow.toArray();
						const QVariantList & listRow = rowData.toVariantList();
						dsRecord.addRow(listRow);
					}
				}
			}
			else
			{
				strMsg = tr("records is not a array!");
				return false;
			}
		}
		else
		{
			strMsg = tr("fields is not a object!");
			return false;
		}
	}
	else
	{
		strMsg = tr("parse data failed, may data is not completed!");
		return false;
	}

	return true;
}

